package com.longtop.sp.mapdao.dialect.oracle;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Method;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.longtop.sp.mapdao.impl.AbstractLob;

/**
 * <p>
 * Title: 用于处理LOB数据，BLOB和CLOB
 * </p>
 * <p>
 * 在程序调用本对象进行<font color='red'>写</font>处理，需要注意如下问题：
 * </p>
 * <ol>
 * <li>conn.setAutoCommit(false); 否则会报“java.sql.SQLException: ORA-01002:
 * 读取违反顺序”，让人不可思议。
 * <li>在定位的select语句中，指明字段名，不能用*。
 * <li>在定位的select语句中，最后用for update锁定记录。
 * <li>使用可更新的 statement，如：conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
 * ResultSet.CONCUR_UPDATABLE);
 * <li>先要用sql语句插入空的lob指针，即empty_blob()和empty_blob()。
 * </ol>
 * 
 * <p>
 * 对于一个已经有数据的lob字段，写数据的真正含义是从位置0起写入新的数据，如果新数据长度 >= 老数据的长度，
 * 老数据将被替换，如果新数据较短，将只替换一部分。如果需要确保替换掉老数据，应先使用sql语句插入空的lob指针。
 * </p>
 * 
 * <p>
 * 综上所述，鉴于写LOB数据的复杂性，建议使用Insert、Update类进行操作。
 * </p>
 * <p>
 * 如果读取LOB中的数据，就没有以上的限制。
 * </p>
 * <p>
 * 在sql语句中，可以使用dbms_lob.getLength(photo)来获取长度。也可是使用PLSQL Developer等工具软件操作lob数据。
 * <p>
 * </p>
 * <p>
 */

public class OracleLob extends AbstractLob {

	// private static org.apache.log4j.Logger log =
	// net.risesoft.commons.log.LogFactory.getLog(OracleLob.class);
	private static final int BUFFER_SIZE = 4096;

	/**
	 * @todo 如何清除lob中的数据？传入一个null还是一个单独的clear()方法？ 问题主要是在这里如何实现，而不是表现形式。
	 */
	/* @todo 对于clob，如果原有的内容长，此方法只改写前面部分，所以，需要先将原有数据清空。对于BLOB，也有同样问题 */

	public OracleLob() {
	}

	protected void writeBLOB(ResultSet rs, Blob b, InputStream is)
			throws SQLException, IOException {
		OutputStream os = null;
		if (b == null) {
			throw new SQLException("刚刚插入或更新的BLOB指针为空！");
		}

		/*
		 * if (WebAppConfig.getServerType() == AppServerTypes.WEBLOGIC) {
		 * if(AppConfig.isUseConnectionPool){ try { os = (
		 * (weblogic.jdbc.vendor.oracle.OracleThinBlob)
		 * b).getBinaryOutputStream(); } catch (Exception ex) {
		 * log.error("使用WebLogic的连接池写BLOB时类型转换错误！" + b.getClass()); } }else{ os = (
		 * (oracle.sql.BLOB) b).getBinaryOutputStream(); } } else { os = (
		 * (oracle.sql.BLOB) b).getBinaryOutputStream(); }
		 */

		// <modify author="jdyao" time="2005-07-26">
		try {
			Method methodToInvoke = b.getClass().getMethod(
					"getBinaryOutputStream", (Class[]) null);
			os = (OutputStream) methodToInvoke.invoke(b, (Object[]) null);
		} catch (Exception e) {
			throw new SQLException(e.getMessage());
		}
		// os = ( (oracle.sql.BLOB) b).getBinaryOutputStream();
		// </modify>

		BufferedOutputStream bos = new BufferedOutputStream(os);
		BufferedInputStream bis = new BufferedInputStream(is);
		byte buf[] = new byte[BUFFER_SIZE];
		int len = -1;
		while ((len = bis.read(buf)) != -1) {
			bos.write(buf, 0, len);
		}
		bis.close();
		bos.flush();
		bos.close();
	}

	protected void writeCLOB(ResultSet rs, Clob c, Reader rd)
			throws java.sql.SQLException, java.io.IOException {
		Writer wr = null;
		if (c == null) {
			throw new SQLException("刚刚插入或更新的CLOB指针为空！");
		}
		/*
		 * if (WebAppConfig.getServerType() == AppServerTypes.WEBLOGIC) {
		 * if(AppConfig.isUseConnectionPool){ try { wr = (
		 * (weblogic.jdbc.vendor.oracle.OracleThinClob)
		 * c).getCharacterOutputStream(); } catch (Exception ex) {
		 * log.error("使用WebLogic的连接池写CLOB时类型转换错误！" + c.getClass()); } }else{ wr = (
		 * (oracle.sql.CLOB) c).getCharacterOutputStream(); } } else { wr = (
		 * (oracle.sql.CLOB) c).getCharacterOutputStream(); }
		 */

		// <modify author="jdyao" time="2005-07-26">
		try {
			Method methodToInvoke = c.getClass().getMethod(
					"getCharacterOutputStream", (Class[]) null);
			wr = (Writer) methodToInvoke.invoke(c, (Object[]) null);
		} catch (Exception e) {
			throw new SQLException(e.getMessage());
		}
		// wr = ((oracle.sql.CLOB) c).getCharacterOutputStream();
		// </modify>

		BufferedWriter bw = new BufferedWriter(wr);
		BufferedReader br = new BufferedReader(rd);
		char buf[] = new char[BUFFER_SIZE];
		int len = -1;
		while ((len = br.read(buf)) != -1) {
			bw.write(buf, 0, len);
		}
		br.close();
		bw.flush();
		bw.close();
	}

	protected char[] readCLOB(ResultSet rs, Clob c) throws SQLException,
			IOException {
		if (c == null) {
			return null;
		}
		Reader r = c.getCharacterStream();
		if (r == null) {
			return null;
		}

		BufferedReader br = new BufferedReader(r);
		char[] charBuf = new char[(int) c.length()];
		br.read(charBuf);
		br.close();
		return charBuf;

	}

	protected byte[] readBLOB(ResultSet rs, Blob b) throws SQLException,
			IOException {
		if (b == null) {
			return null;
		}
		InputStream is = b.getBinaryStream();
		if (is == null) {
			return null;
		}

		BufferedInputStream bis = new BufferedInputStream(is);
		byte[] buf = new byte[(int) b.length()];
		bis.read(buf);
		bis.close();
		return buf;
	}

}
